#!/usr/bin/env python

import sys
import re

from ctypes import *

def hex2float(s):
    i = long(s, 16)                   # convert from hex to a Python int
    cp = pointer(c_ulonglong(i))           # make this into a c integer
    fp = cast(cp, POINTER(c_double))  # cast the int pointer to a float pointer
    return fp.contents.value         # dereference the pointer, get the float


class LLVMFun:
    def __init__(self, resTy, name, args, insts):
        self.resTy = resTy
        self.name  = name
        self.args  = args
        self.insts = insts

    def __str__(self):
        s = "(Fun) %s %s(%s)" % (self.resTy, self.name, self.args)
        for inst in self.insts:
            s += "%s\n" % inst
        return s

class LLVMInst:
    def __init__(self, op, dst, srcs):
        self.op   = op
        self.dst  = dst
        self.srcs = srcs

    def __str__(self):
        s = "%s : %s, %s" % (self.op, self.dst, self.srcs)
        return s

def parseCall(l):
    print "=================="
    print l

    # parse fun call
    m = re.match('.*call (\w+) @(\w+)\((.*)\)', l)
    print m.groups()
    print "=================="

    resTy = m.groups()[0]
    name  = m.groups()[1]
    argStrs = m.groups()[2].split(',')

    args = []
    args.append(resTy)
    args.append(name)
    for a in argStrs:
        (ty, name) = a.split()
        print (ty, name)

        args.append((ty, name))


    return LLVMInst('call', None, args)

def parseBinOp(t):

    ty = t[3]
    op   = t[2]
    dst  = t[0]
    src0 = t[4][:-1]    # drop ','
    src1 = t[5]

    if src0[0] == '0' and src0[1] == 'x':
        src0 = hex2float(src0[2:])
    if src1[0] == '0' and src1[1] == 'x':
        src1 = hex2float(src1[2:])

    return LLVMInst(op, dst, [src0, src1, ty])

def parseFcmp(t):

    cond = t[3]
    dst  = t[0]
    src0 = t[5][:-1]    # drop ','
    src1 = t[6]

    if src0[0] == '0' and src0[1] == 'x':
        src0 = hex2float(src0[2:])
    if src1[0] == '0' and src1[1] == 'x':
        src1 = hex2float(src1[2:])

    return LLVMInst('fcmp', dst, [cond, src0, src1])

def parseAlloca(t):
    
    ty = t[3][:-1]       # drop ','

    return LLVMInst('alloca', t[0], [ty])

def parseLoad(t):

    ty  = t[3][:-1] # drop '*'
    src = t[4][:-1] # drop ','
    dst = t[0]
 
    return LLVMInst('load', dst, [src, ty])

def parseBr(t):

    print t

    if len(t) == 3:

        dst = t[2]

        return LLVMInst('br', dst, [])
        
    else:

        dst = None
        src0 = t[2][:-1]    # cond
        src1 = t[4][:-1]    # then
        src2 = t[6]         # end

        return LLVMInst('br', dst, [src0, src1, src2])

def parseRet(t):

    if t[1] == 'void':
        return LLVMInst('ret', None, [])

    return LLVMInst('ret', t[2], [])

def parseStore(t):

    src = t[2][:-1] # drop ','
    dst = t[4][:-1] # drop ','
 
    return LLVMInst('store', dst, [src])

def parseExpr(l, ll):
    
    inst = None

    t = l.split()

    binops = ["fadd", "fsub", "fmul", "fdiv"]

    if t[0] == 'store':
        inst = parseStore(t)
    elif t[0] == 'br':
        inst = parseBr(t)
    elif t[0] == 'ret':
        inst = parseRet(t)
    elif t[0][-1] == ':':   # label def
        inst = LLVMInst('label', t[0][0:-1], [])
    elif len(t) > 3 and t[2] == 'alloca':
        inst = parseAlloca(t)
    elif len(t) > 3 and t[2] == 'load':
        inst = parseLoad(t)
    elif len(t) > 3 and t[2] == 'call':
        inst = parseCall(l)
    elif len(t) > 3 and (t[2] in binops):
        inst = parseBinOp(t)
    elif len(t) > 3 and t[2] == 'fcmp':
        inst = parseFcmp(t)

    return inst

def parseStmt(ll):

    stms = []

    # parse stmt
    while len(ll) > 0:

        l = ll.pop(0)
        t = l.split()
        if len(t) == 0: continue
        print t
        if t[0] == '}':
            break

        inst = parseExpr(l, ll)
        
        if inst is not None:
            stms.append(inst)

    return stms

def parseFun(l, ll):

    t = l.split()

    # parse signature
    m = re.match('define (\w+) @(\w+)\((.*)\)', l)
    print m.groups()

    argStrs = m.groups()[2].split(',')

    args = []
    for a in argStrs:
        (ty, name) = a.split()
        print (ty, name)

        args.append((ty, name))

    resTy = m.groups()[0]
    name  = m.groups()[1]

    stms  = parseStmt(ll)

    for inst in stms:
        print inst

    fn = LLVMFun(resTy, name, args, stms)

    return fn


def parse(ll):

    fns = []

    while len(ll) > 0:
    
        l = ll.pop(0)
        t = l.split()

        if len(t) < 1: continue

        if t[0] == 'define':
            fn = parseFun(l, ll)

            fns.append(fn)


    return fns

def parseLLVMIR(fname):

    f = open(fname, "r")

    llcodes = f.readlines()

    f.close()

    fns = parse(llcodes)

    print fns

    return fns


def main():

    print "llparse"

    f = open(sys.argv[1], "r")

    codes = f.readlines()

    f.close()

    fns = parse(codes)

    print fns

    print hex2float("3FD470BF40000000")

if __name__ == '__main__':
    main()
